package 多线程.多线程创建方式三;

// 方式三: 利用Callable接口、FutureTask类来实现
// 1. 创建任务对象
//     a. 定义一个类实现Callable接口, 重写call方法, 封装要做的实行, 和要返回的数据
//     b. 把Callable类型的对象封装成FutureTask(线程任务对象)
// 2. 把线程任务对象交给Thread对象
// 3. 调用Thread对象的start方法启动线程
// 4. 线程执行完毕后、通过FutureTask对象的get方法去获取线程任务执行的结果

// FutureTask的API
// 1. public FutureTask<>(Callable call): 把Callable对象封装成FutureTask对象
// 2. public V get() throws Exception: 获取线程执行call方法返回的结果

// 方式三的优缺点
// 优点: 线程任务类只是实现接口, 可以继续继承类和实现接口, 扩展性强; 可以再线程执行完毕后去获取线程执行的结果
// 缺点: 编码复杂一点


import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class Test {
    public static void main(String[] args) throws Exception {
        // 为什么会有方式三: 因为前两种线程创建方式都存在的一个问题: 假如线程执行完毕后有一些数据需要返回, 他们重写的run方法均不能直接返回结果
        // 如何解决这个问题
        // JDK 5.0提供了Callable接口和FutureTask类来实现(多线程的第三种创建方式).
        // 这种方式最大的优点: 可以返回 线程执行完毕后的结果

        // 3. 创建一个Callable的对象
        Callable<String> call = new MyCallable(100);

        // 4. 把Callable的对象封装成一个FutureTask对象(任务对象)
        FutureTask<String> f1 = new FutureTask<>(call);
        // 未来对象(FutureTask)的作用?
        // a. 是一个任务对象, 实现了Runnable对象
        // b. 可以再线程执行完毕之后, 用未来任务对象调用get方法获取线程执行完毕后的结果

        // 5. 把任务对象交给一个Thread对象处理
        new Thread(f1).start();

        // 6. 获取线程执行完毕后返回的结果
        // 注意: 如果执行到这里, 假如上面的子线程还没有执行完毕
        //      这里代码会暂停, 等待上面的子线程执行完毕后才会获取结果
        String rs = f1.get();
        System.out.println(rs);

    }
}
